//
// Created by huxin on 2020/11/3.
//

#ifndef HPSOCKET_MJPGSENDER_H
#define HPSOCKET_MJPGSENDER_H

#include <string>

#include "Http/HPSocketHttpWrap.h"
#include <opencv2/opencv.hpp>

#include <string>
#include <map>
#include <algorithm>

const std::string TEST_CHANNEL = "/test";

template<typename T = std::string>
class ChannelManager {
public:
    ChannelManager() {

    }

    virtual ~ChannelManager() {

    }

protected:
    virtual bool createPubChannel(const std::string &channel) {
        std::lock_guard<std::mutex> locker(m_mxtChannel);
        auto iter = m_mapChannelRecord.find(channel);
        if (iter == m_mapChannelRecord.end()) {
            m_mapChannelRecord[channel] = {};
        }
        return true;
    }

    virtual std::list<std::string> channles() {
        std::list<std::string> channles;
        std::lock_guard<std::mutex> locker(m_mxtChannel);
        if (!m_mapChannelRecord.empty()) {
            for (auto &key : m_mapChannelRecord) {
                channles.push_back(key.first);
            }
        }
        return channles;
    }

    virtual std::list<T> channelMems(const std::string &channel) {
        std::list<T> mems;
        {
            std::lock_guard<std::mutex> locker(m_mxtChannel);
            auto iter = m_mapChannelRecord.find(channel);
            if (iter != m_mapChannelRecord.end()) {
                mems = iter->second;
            }
        }

        return mems;
    }

    virtual bool delChannel(const std::string &channel) {
        std::lock_guard<std::mutex> locker(m_mxtChannel);
        if (!m_mapChannelRecord.empty()) {
            return m_mapChannelRecord.erase(channel) > 0;
        }
        return false;
    }

    virtual void delChannelMem(const std::string &channel, const T &elem) {
        std::lock_guard<std::mutex> locker(m_mxtChannel);
        if (!m_mapChannelRecord.empty()) {
            auto iter = m_mapChannelRecord.find(channel);
            if (iter != m_mapChannelRecord.end()) {
                iter->second.remove(elem);
            }
        }
    }

    virtual bool addChannelMem(const std::string &channel, const T &elem) {
        std::lock_guard<std::mutex> locker(m_mxtChannel);
        auto iter = m_mapChannelRecord.find(channel);
        if (iter != m_mapChannelRecord.end()) {
            iter->second.push_back(elem);
            return true;
        }

        return false;
    }

    std::mutex m_mxtChannel;
    std::map<std::string, //channel
            std::list<T>> m_mapChannelRecord;
};

class MJPGSender : public ChannelManager<CONNID> {
    using superType = ChannelManager<CONNID>;
public:
    explicit MJPGSender(HttpServerWrap &listener);

    virtual ~MJPGSender();

    bool release();

    void pubTestFrame();

    bool open();

    bool isOpened();

    bool publish(const std::string &channel, const cv::Mat &frame);

    void init(const std::string &listenAddr, int port = 8080, int timeout = 400000, int quality = 30);
public:
    virtual bool createPubChannel(const std::string &channel) override;

    virtual bool delChannel(const std::string &channel) override;

    std::list<std::string> channles() override;
protected:
    virtual bool addChannelMem(const std::string &channel, const CONNID &elem) override;

private:
    int m_iPort, m_iTimeout, m_iQuality;
    std::string m_strListenAddr;
    std::mutex m_mxtSubChannel;
    HttpServerWrap &m_listener;
};

#endif //HPSOCKET_MJPGSENDER_H
